"use strict";
var __assign = (this && this.__assign) || function () {
    __assign = Object.assign || function(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                t[p] = s[p];
        }
        return t;
    };
    return __assign.apply(this, arguments);
};
var target = null;
var targetStack = [];
function pushTarget(watchTarget) {
    // 将上一次的 Watcher 存入 targetStack
    targetStack.push(watchTarget);
    target = watchTarget;
}
function popTarget() {
    targetStack.pop();
    target = targetStack[targetStack.length - 1];
}
var Observe = /** @class */ (function () {
    function Observe(value) {
        this.value = value;
        if (Object.prototype.toString.call(this.value).slice(8, -1) === 'Object') {
            this.walk();
        }
        else {
            //  rewrite Array method
        }
    }
    Observe.prototype.walk = function () {
        observe(this.value);
    };
    return Observe;
}());
function observe(obj) {
    if (typeof obj !== 'object') {
        return false;
    }
    Object.keys(obj).forEach(function (key) {
        defineReactive(obj, key);
    });
}
function defineReactive(obj, key) {
    var val = obj[key];
    var dep = new Dep();
    Reflect.defineProperty(obj, key, {
        get: function () {
            dep.depend();
            return val;
        },
        set: function (newVal) {
            if (newVal != val) {
                val = newVal;
                dep.notify();
            }
        }
    });
}
var Dep = /** @class */ (function () {
    function Dep() {
        this.subs = new Set();
    }
    //  依赖收集
    Dep.prototype.depend = function () {
        if (target) {
            this.subs.add(target);
        }
    };
    //  通知更新
    Dep.prototype.notify = function () {
        this.subs.forEach(function (watcher) { return watcher.update(); });
    };
    return Dep;
}());
var Watcher = /** @class */ (function () {
    function Watcher(getter, options) {
        this.getter = getter;
        this.lazy = options && (!!options.lazy);
        this.dirty = options && !!(options.lazy);
        this.watch = options && !!(options.watch);
        this.cb = options && (options.cb);
        //  表示计算属性computed 惰性求值
        if (this.lazy) {
            this.dep = new Dep();
        }
        else {
            this.value = this.get();
        }
    }
    Watcher.prototype.get = function () {
        // 收集依赖
        pushTarget(this);
        //  作为computed 的返回值
        var value = this.getter();
        popTarget();
        return value;
    };
    Watcher.prototype.depend = function () {
        var _a;
        (_a = this.dep) === null || _a === void 0 ? void 0 : _a.depend();
    };
    Watcher.prototype.update = function () {
        if (this.lazy) {
            //  当computed更新之后，会 调用notify 来触发computedWatcher 的 update 此时将dirty设置为true，即代表当前的computed的值为脏值 需要重新计算
            this.dirty = true;
        }
        else if (this.watch) {
            var oldVal = this.value;
            this.value = this.get();
            if (oldVal != this.value) {
                this.cb && this.cb(this.value, oldVal);
            }
        }
        else {
            this.get();
        }
    };
    Watcher.prototype.evaluate = function () {
        var _a;
        this.value = this.get();
        this.dirty = false;
        //  当重新计算computed的值之后 将dirty重置为false ，并通知依赖当前computed的watcher更新
        (_a = this.dep) === null || _a === void 0 ? void 0 : _a.notify();
    };
    return Watcher;
}());
var computedWatcherOption = { lazy: true };
function computed(fn) {
    var def = {};
    var computedWatcher = new Watcher(fn, computedWatcherOption);
    Reflect.defineProperty(def, 'value', {
        get: function () {
            if (computedWatcher.dirty) {
                computedWatcher.evaluate();
            }
            if (target) {
                computedWatcher.depend();
            }
            return computedWatcher.value;
        }
    });
    return def;
}
var watchWatcherOption = { watch: true };
function watch(getter, cb) {
    var watchWatcher = new Watcher(getter, __assign(__assign({}, watchWatcherOption), { cb: cb }));
}
//  computed
